page.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. 'use client';
  2. import { use, useEffect, useState, Suspense } from 'react';
  3. import { useSearchParams, useRouter } from 'next/navigation';
  4. import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
  5. import { Button } from '@/components/ui/button';
  6. import { CheckCircle, ArrowRight, Home } from 'lucide-react';
  7. import { useTranslations } from 'next-intl';
  8. import { toast } from '@/hooks/use-toast';
  9. interface PaymentSuccessPageProps {
  10. params: Promise<{
  11. locale: string;
  12. }>;
  13. }
  14. function PaymentSuccessContent({ locale }: { locale: string }) {
  15. const searchParams = useSearchParams();
  16. const router = useRouter();
  17. const [isLoading, setIsLoading] = useState(true);
  18. const [sessionData, setSessionData] = useState<any>(null);
  19. const t = useTranslations('payment.success');
  20. useEffect(() => {
  21. const sessionId = searchParams.get('session_id');
  22. if (!sessionId) {
  23. router.push(`/${locale}/`);
  24. return;
  25. }
  26. // 验证支付会话
  27. const verifySession = async () => {
  28. try {
  29. const response = await fetch(`/api/verify-payment?session_id=${sessionId}`);
  30. const data = await response.json();
  31. if (response.ok) {
  32. setSessionData(data);
  33. } else {
  34. console.error('Payment verification failed:', data.error);
  35. toast({
  36. title: 'Payment Verification Error',
  37. description: data.error || 'Payment verification failed',
  38. variant: 'destructive'
  39. });
  40. router.push(`/${locale}/payment/failed`);
  41. }
  42. } catch (error) {
  43. console.error('Error verifying payment:', error);
  44. toast({
  45. title: 'Payment Verification Error',
  46. description: 'An error occurred while verifying payment',
  47. variant: 'destructive'
  48. });
  49. router.push(`/${locale}/payment/failed`);
  50. } finally {
  51. setIsLoading(false);
  52. }
  53. };
  54. verifySession();
  55. }, [searchParams, router, locale, toast]);
  56. if (isLoading) {
  57. return (
  58. <div className="min-h-screen flex items-center justify-center bg-gray-50">
  59. <div className="text-center">
  60. <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
  61. <p className="mt-4 text-gray-600">{t('processing')}</p>
  62. <p className="text-sm text-gray-500 mt-2">{t('processingDesc')}</p>
  63. </div>
  64. </div>
  65. );
  66. }
  67. return (
  68. <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
  69. <Card className="w-full max-w-md">
  70. <CardHeader className="text-center pb-4">
  71. <div className="mx-auto mb-4 w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
  72. <CheckCircle className="w-8 h-8 text-green-600" />
  73. </div>
  74. <CardTitle className="text-2xl font-bold text-green-600">
  75. {t('title')}
  76. </CardTitle>
  77. <p className="text-gray-600 mt-2">
  78. {t('subtitle')}
  79. </p>
  80. </CardHeader>
  81. <CardContent className="space-y-6">
  82. <div className="bg-gray-50 rounded-lg p-4">
  83. <h3 className="font-semibold text-gray-900 mb-3">{t('subscriptionDetails')}</h3>
  84. <div className="space-y-2 text-sm">
  85. <div className="flex justify-between">
  86. <span className="text-gray-600">{t('subscriptionPlan')}:</span>
  87. <span className="font-medium">{t('proVersion')}</span>
  88. </div>
  89. <div className="flex justify-between">
  90. <span className="text-gray-600">{t('monthlyCredits')}:</span>
  91. <span className="font-medium">{t('credits800')}</span>
  92. </div>
  93. <div className="flex justify-between">
  94. <span className="text-gray-600">{t('subscriptionFee')}:</span>
  95. <span className="font-medium">{t('pricePerMonth')}</span>
  96. </div>
  97. <div className="flex justify-between">
  98. <span className="text-gray-600">{t('status')}:</span>
  99. <span className="font-medium text-green-600">{t('activated')}</span>
  100. </div>
  101. </div>
  102. </div>
  103. <div className="space-y-3">
  104. <Button
  105. className="w-full bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700"
  106. onClick={() => router.push(`/${locale}/dashboard`)}
  107. >
  108. <Home className="w-4 h-4 mr-2" />
  109. {t('backToDashboard')}
  110. </Button>
  111. <Button
  112. variant="outline"
  113. className="w-full"
  114. onClick={() => router.push(`/${locale}#demo`)}
  115. >
  116. <ArrowRight className="w-4 h-4 mr-2" />
  117. {t('startUsing')}
  118. </Button>
  119. </div>
  120. <div className="text-center text-sm text-gray-500">
  121. <p>{t('thankYou')}</p>
  122. <p className="mt-1">{t('autoRenewal')}</p>
  123. </div>
  124. </CardContent>
  125. </Card>
  126. </div>
  127. );
  128. }
  129. export default function PaymentSuccessPage({ params }: PaymentSuccessPageProps) {
  130. const { locale } = use(params);
  131. return (
  132. <Suspense fallback={
  133. <div className="min-h-screen flex items-center justify-center bg-gray-50">
  134. <div className="text-center">
  135. <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
  136. <p className="mt-4 text-gray-600">Loading...</p>
  137. </div>
  138. </div>
  139. }>
  140. <PaymentSuccessContent locale={locale} />
  141. </Suspense>
  142. );
  143. }